let timer = null;

// 根据xpath查找所有元素
function getElementsByXPath(xpath) {
    const results = [];
    const iterator = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);

    let node = iterator.iterateNext();
    while (node) {
        results.push(node);
        node = iterator.iterateNext();
    }

    return results;
}

// 获取全部元素
function getElementsToMap(patterns) {
    const map = new Map();

    for (const pattern of patterns) {
        const { type, path, description } = pattern;
        let elements = [];
        if (type === 'selector') {
            elements = document.querySelectorAll(path);
        } else if (type === 'xpath') {
            elements = getElementsByXPath(path);
        }
        elements.forEach((element, index) => {
            if (!map.has(element)) {
                map.set(element, new Set());
            }
            map.get(element).add({ type, path, index, description });
        });
    }

    return map;
}

// 验证器
const validator = {
    unique(patterns, options) {
        const { attributes, ignore } = options;
        const res = {
            validator: 'unique-validator',
            description: '验证属性值是否唯一',
            data: [],
        };

        const elementMap = getElementsToMap(patterns);

        for (const attribute of attributes) {
            const valueMap = new Map();

            for (const elementItem of elementMap) {
                const [element] = elementItem;
                const value = element.getAttribute(attribute);
                if (ignore.includes(value)) {
                    continue;
                }
                if (valueMap.has(value)) {
                    const pre = valueMap.get(value);
                    valueMap.set(value, pre + 1);
                } else {
                    valueMap.set(value, 1);
                }
            }

            const data = [];
            for (const value of valueMap) {
                const [att, count] = value;
                if (count > 1) {
                    data.push({
                        attribute,
                        value: att,
                        count,
                    });
                }
            }

            res.data.push({
                attribute,
                data,
                message: data.size > 0 ? `验证不通过，存在${attribute}属性值不唯一的元素` : `验证通过，所有元素的${attribute}属性值唯一`,
            });
        }

        return res;
    },
    exist(patterns, options) {
        const { attributes } = options;
        const res = {
            validator: 'exist-validator',
            description: '验证属性值是否存在',
            data: [],
        };

        const elementMap = getElementsToMap(patterns);

        for (const attribute of attributes) {
            const data = {
                attribute,
                data: [],
                message: `验证通过，所有元素的${attribute}属性都存在`,
            };
            for (const elementItem of elementMap) {
                const [element, paths] = elementItem;
                const attValue = element.getAttribute(attribute);
                if (attValue === null) {
                    data.data.push({
                        name: '元素',
                        paths: [...paths],
                    });
                }
            }
            if (data.data.length > 0) {
                data.message = `验证未通过，存在${attribute}属性不存在的元素`;
            }
            res.data.push(data);
        }

        return res;
    },
};

/** 创建自定义元素 */
function createCustomElement(rootNode, options) {
    const { left, top, width, height, text, background = '#ff0000', color = '#ffffff', borderRadius = '', padding = '' } = options;

    const eleNode = document.createElement('div');

    const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    const scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;

    eleNode.style.left = typeof left === 'number' ? `${left + scrollLeft}px` : left + scrollLeft;
    eleNode.style.top = typeof top === 'number' ? `${top + scrollTop}px` : top + scrollTop;
    eleNode.style.width = typeof width === 'number' ? `${width}px` : width;
    eleNode.style.height = typeof height === 'number' ? `${height}px` : height;

    eleNode.style.background = background;
    eleNode.style.color = color;
    eleNode.style.position = 'absolute';
    eleNode.style.zIndex = '99999';
    eleNode.style.pointerEvents = 'none';
    eleNode.style.borderRadius = borderRadius;
    eleNode.style.padding = padding;
    eleNode.className = 'rpa-element-stroke';

    if (text) {
        eleNode.innerHTML = text;
        eleNode.style.lineHeight = '14px';
        eleNode.style.fontSize = '14px';
    }

    rootNode.appendChild(eleNode);

    return [eleNode];
}

// 高亮元素
const elementStroke = (ele, strokeType = 'outer') => {
    // 目标元素位置信息
    const { left, top, width, height } = ele.getBoundingClientRect();
    // 目标元素的元素类型
    const tagName = ele.tagName.toLocaleLowerCase();
    // 目标元素的根元素
    const rootNode = document?.body;

    const clamp = (n, minn, maxn) => (n < minn ? minn : n > maxn ? maxn : n);

    // 边框宽度
    const lineThickness = 2;

    let topBorderStyle = {};
    let bottomBorderStyle = {};
    let leftBorderStyle = {};
    let rightBorderStyle = {};
    let tagBlockStyle = {};

    // 样式设计

    const topBorderOuter = {
        left: left - lineThickness / 2,
        top: top - lineThickness,
        width: width + lineThickness,
        height: lineThickness,
    };

    const topBorderInner = {
        left,
        top,
        width,
        height: lineThickness,
    };

    const bottomBorderInner = {
        left,
        top: top + height - lineThickness,
        width,
        height: lineThickness,
    };

    const bottomBorderOuter = {
        left: left - lineThickness / 2,
        top: top + height,
        width: width + lineThickness,
        height: lineThickness,
    };

    const leftBorderInner = {
        left,
        top,
        width: lineThickness,
        height,
    };

    const leftBorderOuter = {
        left: left - lineThickness,
        top: top - lineThickness,
        width: lineThickness,
        height: height + 2 * lineThickness,
    };

    const rightBorderInner = {
        left: left + width - lineThickness,
        top,
        width: lineThickness,
        height,
    };

    const rightBorderOuter = {
        left: left + width,
        top: top - lineThickness,
        width: lineThickness,
        height: height + 2 * lineThickness,
    };

    const tagBlockInner = {
        left: clamp(left, 0, window.innerWidth - width - 2 * lineThickness),
        top: clamp(top, 0, window.innerHeight - height - 2 * lineThickness),
        width: 'fit-content',
        height: 'fit-content',
        text: tagName,
        padding: '0 6px 0 4px',
    };

    const tagBlockOuter = {
        left: clamp(left - lineThickness, 0, window.innerWidth - width - 2 * lineThickness),
        top: clamp(top - lineThickness - 14, 0, window.innerHeight - height - 2 * lineThickness),
        width: 'fit-content',
        height: 'fit-content',
        text: tagName,
        padding: '0 4px',
    };

    switch (strokeType) {
        case 'inner':
            topBorderStyle = topBorderInner;
            bottomBorderStyle = bottomBorderInner;
            leftBorderStyle = leftBorderInner;
            rightBorderStyle = rightBorderInner;
            tagBlockStyle = tagBlockInner;
            break;
        default:
            topBorderStyle = topBorderOuter;
            bottomBorderStyle = bottomBorderOuter;
            leftBorderStyle = leftBorderOuter;
            rightBorderStyle = rightBorderOuter;
            tagBlockStyle = tagBlockOuter;
            break;
    }

    // 上边框
    createCustomElement(rootNode, topBorderStyle);

    // 下边框
    createCustomElement(rootNode, bottomBorderStyle);

    // 左边框
    createCustomElement(rootNode, leftBorderStyle);

    // 右边框
    createCustomElement(rootNode, rightBorderStyle);

    // 元素遮罩
    createCustomElement(rootNode, {
        left,
        top,
        width,
        height,
        background: 'rgba(255,0,0,0.1)',
    });

    // 元素类型
    createCustomElement(rootNode, tagBlockStyle);
    timer = setTimeout(() => {
        hideStroke();
    }, 3000);
};

// 验证
function validate(rules) {
    const res = [];
    rules.forEach((rule) => {
        rule.resolver?.forEach((u) => {
            const [action] = u.validator.split('-');
            const result = validator[action](rule.patterns, u);
            res.push(result);
        });
    });
    return res;
}

function hideStroke() {
    const exitElements = document.querySelectorAll('.rpa-element-stroke');
    exitElements.forEach((element) => {
        element.remove();
    });
}

// 高亮元素
function highlight(path, type, index) {
    if (timer) {
        clearTimeout(timer);
        timer = null;
    }
    // 取消之前的高亮
    hideStroke();

    let elements = [];
    if (type === 'selector') {
        elements = document.querySelectorAll(path);
    } else if (type === 'xpath') {
        elements = getElementsByXPath(path);
    }

    const INDEX = Number(index);
    // 设置现在的高亮
    if (INDEX > -1) {
        const element = elements[INDEX];
        if (element) {
            element.scrollIntoView();
            elementStroke(element, 'inner');
        }
    } else {
        elements[0].scrollIntoView();
        let i = 0;
        for (const element of elements) {
            elementStroke(element, 'inner');
            i += 1;
            if (i > 10) {
                break;
            }
        }
    }
}

function onWheel() {
    if (timer) {
        clearTimeout(timer);
        timer = null;
    }

    hideStroke();
}

document.addEventListener('wheel', onWheel);

// 暴露给background执行脚本使用
const uiaActions = {
    validate,
    highlight,
};

console.log(chrome, 'chrome.runtime');

setTimeout(() => {
    try {
        const shadowRoots = chrome.dom.openOrClosedShadowRoot(document.body);
        console.log(shadowRoots, 'shadowRoots');
    } catch (error) {}
}, 5000);
